package com.lanhe.common.utils.wechat;

import com.lanhe.common.utils.StringUtils;
import com.lanhe.common.utils.wechat.vo.PrePayVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.security.MessageDigest;
import java.util.*;

import static com.lanhe.common.utils.StringUtils.MD5;


/**
 * 微信支付配置
 *
 * @author zhanggl
 * @date 2024/1/5
 */
@Component
@Slf4j
public class WxPayUtil {

	/**
	 * 统一下单地址
	 */
	public static final String API_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

	/**
	 * appid
	 */
	@Value("${lanhe.wechat.appid}")
	private String APP_ID;

	/**
	 * 商户id
	 */
	@Value("${lanhe.wechat.mchid}")
	private String MCH_ID;

	/**
	 * 附加数据
	 */
	public static String ATTCH = "";

	/**
	 * 发起请求的服务器ip
	 */
	@Value("${lanhe.wechat.spbillCreateIp}")
	private String SPBILL_CREATE_IP;

	/**
	 * apiv2key
	 */
	@Value("${lanhe.wechat.apiV2}")
	private String MCH_KEY;

	/**
	 * 通知回调地址
	 */
	@Value("${lanhe.wechat.notifyUrl}")
	private String NOTIFY_URL;
	public static String TRADE_TYPE = "APP";

	/**
	 * 生成随机字符串
	 *
	 * @return java.lang.String
	 * @author zhanggl
	 * @date 2024/1/5
	 */
	public String getNonceStr() {
		String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 32; i++) {
			sb.append(str.charAt((int) (Math.random() * str.length())));
		}
		return sb.toString();
	}

	/**
	 * 生成商户订单号
	 *
	 * @return java.lang.String
	 * @author zhanggl
	 * @date 2024/1/5
	 */
	public String getOutTradeNo() {
		return MCH_ID + System.currentTimeMillis();
	}

	/**
	 * 预下单-第一次签名
	 *
	 * @return java.lang.String
	 * @author zhanggl
	 * @date 2024/1/5
	 */
	public String prePay(String price,
	                     String body,
	                     String nonceStr,
	                     String outTradeNo) throws Exception {


		long startTime = System.currentTimeMillis();
		Map<String, String> params = new HashMap<>();
		params.put("appid", APP_ID);
		params.put("nonce_str", nonceStr);
		params.put("attach", ATTCH);
		params.put("body", body);
		params.put("mch_id", MCH_ID);
		params.put("notify_url", NOTIFY_URL);
		params.put("out_trade_no", outTradeNo);
		params.put("total_fee", price);

		// 发起请求的服务器ip
		params.put("spbill_create_ip", SPBILL_CREATE_IP);
		params.put("trade_type", TRADE_TYPE);

		// 生成签名
		String key = MCH_KEY; // api2秘钥，现在用的api3
		String sign = sign(params, key);

		//存入签名
		params.put("sign", sign);
		// //封装请求参数
		String xml = "<xml>" +
				"<appid>" + APP_ID + "</appid>" +
				"<attach>" + ATTCH + "</attach>" +
				"<body>" + body + "</body>" +
				"<mch_id>" + MCH_ID + "</mch_id>" +
				"<nonce_str>" + nonceStr + "</nonce_str>" +
				"<notify_url>" + NOTIFY_URL + "</notify_url>" +
				"<out_trade_no>" + outTradeNo + "</out_trade_no>" +
				"<spbill_create_ip>" + SPBILL_CREATE_IP + "</spbill_create_ip>" +
				"<total_fee>" + price + "</total_fee>" +
				"<trade_type>" + TRADE_TYPE + "</trade_type>" +
				"<sign>" + sign + "</sign>" +
				"</xml>";

		// 发送HTTP POST请求
		HttpClient httpClient = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost(API_URL);
		httpPost.setHeader("Content-Type", "application/xml;charset=UTF-8");
		httpPost.setEntity(new StringEntity(xml.toString(), "UTF-8"));
		HttpResponse httpResponse = httpClient.execute(httpPost);
		HttpEntity httpEntity = httpResponse.getEntity();
		String result = "";
		if (httpEntity != null) {
			result = EntityUtils.toString(httpEntity, "UTF-8");
		}

		log.info("[{}] [prePay] ======>请求 {}", this.getClass().getSimpleName(), API_URL);
		log.info("[{}] [prePay] ======>返回 {}", this.getClass().getSimpleName(), result);
		log.info("[{}] [prePay] ======>耗时 {}", this.getClass().getSimpleName(), (System.currentTimeMillis() - startTime));
		return result;
	}

	/**
	 * 通过prePayId.进行二次签名
	 */
	/**
	 * 预下单-第二次签名
	 *
	 * @return java.lang.String
	 * @author zhanggl
	 * @date 2024/1/5
	 */
	public PrePayVo secondsSign(String prePayId, String nonceStr, long timestamp) {

		Map<String, String> params = new HashMap<String, String>();
		params.put("appid",APP_ID);
		params.put("partnerid", MCH_ID);
		params.put("prepayid", prePayId);
		params.put("package", "Sign=WXPay");
		params.put("noncestr", nonceStr);
		params.put("timestamp", String.valueOf(timestamp));

		// 生成签名
		String key = MCH_KEY;
		String sign = sign(params, key);

		//封装参数
		PrePayVo prePayVo = new PrePayVo();
		prePayVo.setAppid(APP_ID);
		prePayVo.setPartnerid(MCH_ID);
		prePayVo.setPrepayid(prePayId);
		prePayVo.setNoncestr(nonceStr);
		prePayVo.setTimestamp(timestamp);
		prePayVo.setSign(sign);
		return prePayVo;
	}


	/**
	 * 对参数进行签名
	 *
	 * @param params 待签名的参数
	 * @param key    API密钥
	 * @return 签名结果
	 */
	public static String sign(Map<String, String> params, String key) {

		// 按照参数名ASCII码从小到大排序
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		// 拼接参数名和参数值
		StringBuilder sb = new StringBuilder();
		for (String k : keys) {
			String v = params.get(k);
			if (v != null && !"".equals(v.trim())) {
				sb.append(k).append("=").append(v.trim()).append("&");
			}
		}
		sb.append("key=").append(key);

		// 对字符串进行MD5加密
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] bytes = md.digest(sb.toString().getBytes("UTF-8"));
			String sign = bytesToHexString(bytes).toUpperCase();
			return sign;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 将字节数组转换为十六进制字符串
	 *
	 * @param bytes 字节数组
	 * @return 十六进制字符串
	 */
	private static String bytesToHexString(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		for (byte b : bytes) {
			String hex = Integer.toHexString(b & 0xFF);
			if (hex.length() == 1) {
				sb.append("0");
			}
			sb.append(hex);
		}
		return sb.toString();
	}

	/**
	 * 验证回调签名
	 *
	 * @param map 入参
	 * @return boolean
	 * @author zhanggl
	 * @date 2024/1/5
	 */
	public boolean isTenPaySign(Map<String, String> map) {

		String signFromApiResponse = map.get("sign");
		if (StringUtils.isBlank(signFromApiResponse)) {
			System.out.println("API返回的数据签名数据不存在，有可能被第三方篡改!!!");
			return false;
		}
		System.out.println("服务器回包里面的签名是:" + signFromApiResponse);

		//过滤空 设置 TreeMap
		SortedMap<String,String> packageParams = new TreeMap();

		for (String parameter : map.keySet()) {
			String parameterValue = map.get(parameter);
			String v = "";
			if (null != parameterValue) {
				v = parameterValue.trim();
			}
			packageParams.put(parameter, v);
		}
		StringBuffer sb = new StringBuffer();
		Set es = packageParams.entrySet();
		Iterator it = es.iterator();

		while(it.hasNext()) {
			Map.Entry entry = (Map.Entry)it.next();
			String k = (String)entry.getKey();
			String v = (String)entry.getValue();
			if(!"sign".equals(k) && null != v && !"".equals(v)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + MCH_KEY);

		//将API返回的数据根据用签名算法进行计算新的签名，用来跟API返回的签名进行比较
		// 算出签名
		String resultSign = null;
		String tobesign = sb.toString();

		try{
			resultSign = MD5(tobesign).toUpperCase();
		}catch (Exception e) {
			resultSign = MD5(tobesign).toUpperCase();
		}
		String tenpaySign = packageParams.get("sign").toUpperCase();
		return tenpaySign.equals(resultSign);
	}
}

